`timescale 1ns / 1ps
`include "../include/cpu_defines.sv"
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/12 15:13:35
// Design Name: 
// Module Name: InstBuffer1
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module InstBuffer1#(
	BUFFER_SIZE=8,
	BUFFER_WIDTH=$clog2(BUFFER_SIZE))(
    input logic cpu_clk,
	input logic cpu_rst_n,
	input logic clear,
	input logic front_clear,
	input logic stall,

	input logic [1: 0][31: 0] inst_i,
	input logic [1: 0] inst_valid_i,
	input logic [1: 0][31: 0] inst_request_addr,
	input logic inst_data_ok,
	input BranchInfo inst_branch_info_i,
	input logic [1: 0] issue_num,
	input logic tlb_refill_i,
	input logic tlb_invalid_i,
	input logic [1: 0] request_inst_num,

	output logic [1: 0] issue_num_pre,
	output logic [1: 0] inst_en,
	output logic [31: 0] inst1,
	output logic [31: 0] inst2,
	output logic [31: 0] iaddr1,
	output logic [31: 0] iaddr2,
	output logic [1: 0] tlb_refill_o,
	output logic [1: 0] tlb_invalid_o,
	output logic inst_buffer_full,
	output BranchInfo inst_branch_info_o,
	output InstInfoBundle inst_info_bundle1,
	output InstInfoBundle inst_info_bundle2
    );

	typedef struct{
		logic tlb_refill;
		logic tlb_invalid;
		logic [31:0] inst;
		logic [31:0] addr;
		BranchInfo branch_info;
	}ibitem;

	ibitem buffer[BUFFER_SIZE-1: 0];
	InstInfoBundle buffer_bundle[BUFFER_SIZE-1: 0];
	logic [BUFFER_SIZE-1: 0] buffer_en;
	InstInfoBundle inst1_bundle, inst2_bundle;
	logic [BUFFER_WIDTH-1: 0] head, tail, headp1, tailp1, tail1;
	logic [BUFFER_WIDTH: 0] inbuffer_count;
	logic [1: 0] inst_num;
	logic write_enable;
	logic [BUFFER_WIDTH-1: 0] head_pre, tail_pre;

	assign write_enable = inst_data_ok & (inst_valid_i[0] | inst_valid_i[1]);
	assign inst_num = inst_valid_i[0] + inst_valid_i[1];

	assign headp1 = head + 1;
	assign tailp1 = tail + 1;
	assign tail1 = inst_valid_i[0] ? tailp1 : tail;

	always_ff @(posedge cpu_clk)begin
		head_pre <= head + issue_num;
		tail_pre <= tail;
		if(cpu_rst_n == 1'b0 || clear || front_clear)begin
			head <= 0;
			tail <= 0;
			for(int i=0; i<BUFFER_SIZE; i++)begin
				buffer[i].inst <= 0;
				buffer[i].addr <= 0;
				buffer[i].tlb_refill <= 0;
				buffer[i].tlb_invalid <= 0;
				buffer[i].branch_info <= 0;
				buffer_bundle[i] <= 0;
			end
			buffer_en <= 0;
			inbuffer_count <= 0;
		end
		else begin
			head <= head + issue_num;
			inbuffer_count <= inbuffer_count - issue_num + ({2{write_enable}} & inst_num);
			buffer_en <= (buffer_en | 
						({8{inst_data_ok & (inst_valid_i[0] | inst_valid_i[1])}} & (1 << tail)) | 
						({8{inst_data_ok & inst_valid_i[0] & inst_valid_i[1]}} & (1 << tailp1))) &
						(~({8{issue_num[0] | issue_num[1]}} & (1 << head))) &
						(~({8{issue_num[1]}} & (1 << headp1)));
			if(write_enable)begin
				tail <= tail + inst_num;
				if(inst_valid_i[0])begin
					buffer[tail].inst <= inst_i[0];
					buffer[tail].addr <= inst_request_addr[0];
					buffer[tail].tlb_refill <= tlb_refill_i;
					buffer[tail].tlb_invalid <= tlb_invalid_i;
					buffer_bundle[tail] <= inst1_bundle;
					buffer[tail].branch_info <= inst_branch_info_i;
				end
				if(inst_valid_i[1])begin
						buffer[tail1].inst <= inst_i[1];
						buffer[tail1].addr <= inst_request_addr[1];
						buffer[tail1].tlb_refill <= tlb_refill_i;
						buffer[tail1].tlb_invalid <= tlb_invalid_i;
						buffer_bundle[tail1] <= inst2_bundle;
						buffer[tail1].branch_info <= inst_branch_info_i;
				end
			end
		end
	end

	assign inst_en[0] = buffer_en[head];
	assign inst_en[1] = (buffer_en[head] & buffer_bundle[head].multdiv) | 
						(buffer_en[headp1] & (~buffer_bundle[headp1].multdiv)
										& (~(buffer_bundle[head].exception & buffer_bundle[headp1].exception))
										& (~(buffer_bundle[head].j & buffer_bundle[headp1].j)));
	assign issue_num_pre = buffer_en[head] & buffer_en[headp1] & (~(buffer_bundle[head].multdiv | buffer_bundle[headp1].multdiv)) & (~(buffer_bundle[head].j & buffer_bundle[headp1].j)) & (~(buffer_bundle[head].exception & buffer_bundle[headp1].exception)) ? 2'b10 : inst_en[0] ? 2'b01 : 2'b00;

	assign inst1 = buffer[head].inst;
	assign inst2 = buffer[headp1].inst;
	assign iaddr1 = buffer[head].addr;
	assign iaddr2 = buffer[headp1].addr;
	assign tlb_refill_o = {buffer[headp1].tlb_refill, buffer[head].tlb_refill};
	assign tlb_invalid_o = {buffer[headp1].tlb_invalid, buffer[head].tlb_invalid};
	assign inst_branch_info_o = buffer_bundle[head].analyze_result != 2'b00 ? buffer[head].branch_info : buffer[headp1].branch_info;
	assign inst_info_bundle1 = buffer_bundle[head];
	assign inst_info_bundle2 = buffer_bundle[headp1];
	assign inst_buffer_full = inbuffer_count + request_inst_num - issue_num >= BUFFER_SIZE - 3;

	FrontInstAnalyze inst_analyze1(
		.cpu_clk(cpu_clk),
		.cpu_rst_n(cpu_rst_n),
		.pre_inst(inst_i[0]),
		.pre_pc(inst_request_addr[0]),
		.analyze_result(inst1_bundle.analyze_result),
		// .analyze_addr(inst1_bundle.analyze_addr),
		.branch_inst(inst1_bundle.branch),
		.store_inst(inst1_bundle.store),
		.multdiv_inst(inst1_bundle.multdiv),
		.j_inst(inst1_bundle.j),
		.exception_inst(inst1_bundle.exception),
		.ras_we(inst1_bundle.ras_we),
		.ras_re(inst1_bundle.ras_re),
		.jalr(inst1_bundle.jalr)
	);

	FrontInstAnalyze inst_analyze2(
		.cpu_clk(cpu_clk),
		.cpu_rst_n(cpu_rst_n),
		.pre_inst(inst_i[1]),
		.pre_pc(inst_request_addr[1]),
		.analyze_result(inst2_bundle.analyze_result),
		// .analyze_addr(inst2_bundle.analyze_addr),
		.branch_inst(inst2_bundle.branch),
		.store_inst(inst2_bundle.store),
		.multdiv_inst(inst2_bundle.multdiv),
		.j_inst(inst2_bundle.j),
		.exception_inst(inst2_bundle.exception),
		.ras_we(inst2_bundle.ras_we),
		.ras_re(inst2_bundle.ras_re),
		.jalr(inst2_bundle.jalr)
	);
endmodule

module FrontInstAnalyze(
	input logic cpu_clk,
	input logic cpu_rst_n,
	input logic [31: 0] pre_inst,
	input logic [31: 0] pre_pc,
    output logic[1:0] analyze_result,//00代表不是分支，01代表条件跳转，10代表直接直接跳转, 11返回指令
    // output logic[31:0] analyze_addr,//跳转地址
	output logic branch_inst,
	output logic store_inst,
	output logic multdiv_inst,
	output logic j_inst,
	output logic exception_inst,
	output logic ras_we,
	output logic ras_re,
	output logic jalr
	);
    logic [5: 0] op,func;
	logic [31: 0] jump_addr;
	logic [31: 0] signed_imm, branch_addr;
	logic _reg, j, jal, jr, beq, bne, bgez_before, blez, bgtz, bgezal, bltzal;
	logic mult, multu, div, divu, mul, madd, maddu, msub, msubu;
	logic mfc0, mtc0, TLBWI, TLBWR, TLBP, TLBR, _wait, cop0, cache;
	logic absolute_jump, reg_jump;
	logic ras_re_pre, ras_we_pre;
	logic branch;

	assign op = pre_inst[31: 26];
	assign func = pre_inst[5 : 0];
	assign beq =  ~op[5] & ~op[3] & op[2] & ~op[1] & ~op[0] ; // beq || beql
    assign bne =  ~op[5] & ~op[3] & op[2] & ~op[1] & op[0] ; // bne || bnel
	assign bgez_before = ~op[5] & ~op[4] & ~op[3] & ~op[2] & ~op[1] & op[0];
	assign bgtz = ~op[5] & ~op[3] & op[2] & op[1] & op[0]; // bgtz || bgtzl
	assign blez = ~op[5] & ~op[3] & op[2] & op[1] & ~op[0]; // blez || blezl
	assign j =  ~op[5] & ~op[4] & ~op[3] & ~op[2] & op[1] & ~op[0] ;
    assign jal =  ~op[5] & ~op[4] & ~op[3] & ~op[2] & op[1] & op[0] ;
	assign jalr = _reg & ~func[5] & ~func[4] & func[3] & ~func[2] & ~func[1] & func[0];
    assign jr = _reg & ~func[5] & ~func[4] & func[3] & ~func[2] & ~func[1] & ~func[0];
	assign bgezal = bgez_before & pre_inst[20] & pre_inst[16];
	assign bltzal = bgez_before & pre_inst[20] & ~pre_inst[16];
	assign branch = beq | bne | bgez_before | j | jal | bgtz | blez;

	assign mult = _reg & ~func[5] & func[4] & func[3] & ~func[2] & ~func[1] & ~func[0];
    assign multu = _reg & ~func[5] & func[4] & func[3] & ~func[2] & ~func[1] & func[0];
	assign div = _reg & ~func[5] & func[4] & func[3] & ~func[2] & func[1] & ~func[0];
    assign divu = _reg & ~func[5] & func[4] & func[3] & ~func[2] & func[1] & func[0];
	assign mul = ~op[5] & op[4] & op[3] & op[2] & ~op[1] & ~op[0] & ~func[5] & ~func[4] & ~func[3] & ~func[2] & func[1] & ~func[0];
	assign madd = ~op[5] & op[4] & op[3] & op[2] & ~op[1] & ~op[0] & ~func[5] & ~func[4] & ~func[3] & ~func[2] & ~func[1] & ~func[0];
	assign maddu = ~op[5] & op[4] & op[3] & op[2] & ~op[1] & ~op[0] & ~func[5] & ~func[4] & ~func[3] & ~func[2] & ~func[1] & func[0];
	assign msub = ~op[5] & op[4] & op[3] & op[2] & ~op[1] & ~op[0] & ~func[5] & ~func[4] & ~func[3] & func[2] & ~func[1] & ~func[0];
	assign msubu = ~op[5] & op[4] & op[3] & op[2] & ~op[1] & ~op[0] & ~func[5] & ~func[4] & ~func[3] & func[2] & ~func[1] & func[0];

	assign cop0 = ~op[5] & op[4] & ~op[3] & ~op[2] & ~op[1] & ~op[0];
	assign mfc0 = cop0 & ~pre_inst[25] & ~pre_inst[24] & ~pre_inst[23] & ~pre_inst[22] & ~pre_inst[21];
	assign mtc0 = cop0 & ~pre_inst[25] & ~pre_inst[24] & pre_inst[23] & ~pre_inst[22] & ~pre_inst[21];
	assign TLBWI 	= cop0 & pre_inst[25] & ~func[5] & ~func[4] & ~func[3] & ~func[2] & func[1] & ~func[0];
	assign TLBP 	= cop0 & pre_inst[25] & ~func[5] & ~func[4] & func[3] & ~func[2] & ~func[1] & ~func[0];
	assign TLBR 	= cop0 & pre_inst[25] & ~func[5] & ~func[4] & ~func[3] & ~func[2] & ~func[1] & func[0];
	assign TLBWR 	= cop0 & pre_inst[25] & ~func[5] & ~func[4] & ~func[3] & func[2] & func[1] & ~func[0];
	assign _wait 	= cop0 & pre_inst[25] & func[5] & ~func[4] & ~func[3] & ~func[2] & ~func[1] & ~func[0];
	assign cache = op[5] & ~op[4] & op[3] & op[2] & op[1] & op[0];


	assign _reg = ~|op;
	// assign ras_re = ras_re_pre & inst_en;
	// assign ras_we = ras_we_pre & inst_en;
	assign jump_addr = {pre_pc[31: 28], pre_inst[25: 0], 2'b00};
	assign signed_imm = {{14{pre_inst[15]}}, pre_inst[15: 0], 2'b00};
	assign branch_addr = signed_imm + pre_pc + 32'b100;
	always_comb begin
		absolute_jump = j | jal;
		reg_jump = jr | jalr;
		// branch_inst <= beq | bne | bgez_before | bgtz | blez;
		branch_inst = beq | bne | bgez_before | j | jal | bgtz | blez;
		multdiv_inst = mult | multu | div | divu | mul | madd | maddu;
		store_inst = beq | bne | bgez_before | jr | jalr;
		j_inst = beq | bne | bgez_before | bgtz | blez;
		exception_inst = mtc0 | mfc0 | TLBWI | TLBWR | TLBP | TLBR | _wait | cache;
		// signed_imm <= pre_inst[15] == 1'b1 ? {16'hffff, pre_inst[15: 0]} : {16'h0000, pre_inst[15: 0]};
		// jump_addr <= {pre_pc[31: 28], pre_inst[25: 0], 2'b00};
		analyze_result[1] = j | jal | jr | jalr;
		analyze_result[0] = beq | bne | bgez_before | bgtz | blez | jr | jalr;
		ras_we = bgezal | bltzal | jal | jalr;
		ras_re = (jr) & (pre_inst[25: 21] == 5'd31);
		// branch_addr <= (signed_imm << 2) + pre_pc;
		// analyze_addr = {32{j | jal}} & jump_addr | {32{beq | bne | bgez_before | bgtz | blez}} & branch_addr;
	end
endmodule